﻿#pragma once
#include "AsQBasicGlobal.h"
#include "AsQException.h"
#include <QMap>
#include <QList>
#include <QVector>

__AS_JY(Update 2022)
namespace As {

	template<typename InputType>
	bool GetMinMaxLocal(const InputType* inputArray, int length, InputType* min, InputType* max, int* minLocal = nullptr, int* maxLocal = nullptr);

	template<typename InputType, typename OutputType>
	bool ValueMapConvert(const InputType* inputArray, OutputType* outputArray, int length, const QVector<OutputType>& mapTable, InputType inputOffset = 0);

	template<typename InputType, typename OutputType>
	bool ValueMapConvert(const InputType* inputArray, OutputType* outputArray, int length, const QMap<InputType, OutputType>& valueMap, InputType inputOffset = 0);


};
/*
  gets the maximum and minimum values in an array
  notice:
	"min""max""minLocal""maxLocal" can all be replaced by nullptr if you don't want to get these arguments.
*/
template<typename InputType>
bool As::GetMinMaxLocal(const InputType* inputArray, int length, InputType* min, InputType* max, int* minLocal, int* maxLocal) {
	if (inputArray == nullptr || length <= 0) return false;
	const
		InputType*	input_ptr = inputArray;
	InputType	_min = *input_ptr;
	InputType	_max = *input_ptr++;
	int			_minLocal = 0;
	int			_maxLocal = 0;
	try {
		for (int i = 1; i < length; ++i, ++input_ptr) {
			if (_min > *input_ptr) {
				_min = *input_ptr;
				_minLocal = i;
			}
			if (_max < *input_ptr) {
				_max = *input_ptr;
				_maxLocal = i;
			}
		}
	}
	catch (...) {
		throw AsQException(AsException::CodeMethod_ValueMapConvert, "The input data is out of bounds.");
	}
	if (min != nullptr) *min = _min;
	if (max != nullptr) *max = _max;
	if (minLocal != nullptr) *minLocal = _minLocal;
	if (maxLocal != nullptr) *maxLocal = _maxLocal;
	return true;
}
/*
  converts an array value to a mapping table value based on "inputMappingTable".
  notice:
	1.The range of all values in the "inputArray" (+ "inputOffset") should be in the range of the "inputMappingTable" (+ "inputOffset").
	2.In order to ensure the accuracy of the mapping, the input atype should be the integer atype.
*/
template<typename InputType, typename OutputType>
bool As::ValueMapConvert(const InputType* inputArray, OutputType* outputArray, int length, const QVector<OutputType>& inputMappingTable, InputType inputOffset) {
	if (inputArray == nullptr || length <= 0 || inputMappingTable.empty()) return false;
	std::vector<OutputType> MapTable = inputMappingTable.toStdVector();
	const InputType*		input_ptr = inputArray;
	OutputType*				output_ptr = outputArray;
	try {
		if (!inputOffset) {
			for (int i = 0; i < length; ++i, ++input_ptr, ++output_ptr) {
				*output_ptr = MapTable[(uint)*input_ptr];
			}
		}
		else {
			for (int i = 0; i < length; ++i, ++input_ptr, ++output_ptr) {
				*output_ptr = MapTable[(uint)(*input_ptr + inputOffset)];
			}
		}
	}
	catch (...) {
		throw AsQException(AsException::CodeMethod_ValueMapConvert, QString("The input data is out of bounds."));
	}
	return true;
}
/*
  converts an array value to a mapping table value based on "inputMappingTable".
  notice:
	1.The range of all values in the "inputArray" (+ "inputOffset") should be in the range of the "inputMappingTable" (+ "inputOffset").
	2.In order to ensure the accuracy of the mapping, the input atype should be the integer atype.
*/
template<typename InputType, typename OutputType>
bool As::ValueMapConvert(const InputType* inputArray, OutputType* outputArray, int length, const QMap<InputType, OutputType>& inputMappingTable, InputType inputOffset) {
	if (inputArray == nullptr || length <= 0 || inputMappingTable.empty()) return false;
	QList<InputType >			keys = inputMappingTable.keys();
	QList<OutputType>			values = inputMappingTable.values();
	int							size = keys.back() + inputOffset + 1;
	OutputType*					MapTable = new OutputType[size];
	QList<InputType >::iterator key;
	QList<InputType >::iterator key_next;
	QList<OutputType>::iterator value;
	QList<OutputType>::iterator value_next;
	if (inputOffset) {
		for (auto k = keys.begin(); k != keys.end(); ++k) {
			*k += inputOffset;
		}
		if (keys.front() < 0) return false;
	}
	for (int i = 0; i < size; i++) {
		key = keys.begin();
		value = values.begin();
		key_next = key + 1;
		value_next = value + 1;
		while (true) {
			if (key_next == keys.end()) {
				MapTable[i] = i;
				break;
			}
			if (i >= *key && i <= *key_next) {
				MapTable[i] = (int)(((double)(i - *key) / (*key_next - *key)) * (*value_next - *value)) + *value;
				break;
			}
			else {
				++key;
				++value;
				++key_next;
				++value_next;
			}
		}
	}
	const
		InputType*  input_ptr = inputArray;
	OutputType* output_ptr = outputArray;
	try {
		if (!inputOffset) {
			for (int i = 0; i < length; ++i, ++input_ptr, ++output_ptr) {
				*output_ptr = MapTable[(uint)*input_ptr];
			}
		}
		else {
			for (int i = 0; i < length; ++i, ++input_ptr, ++output_ptr) {
				*output_ptr = MapTable[(uint)(*input_ptr + inputOffset)];
			}
		}
	}
	catch (...) {
		delete MapTable;
		throw AsQException(AsException::CodeMethod_ValueMapConvert, QString("The input data is out of bounds."));
	}
	delete MapTable;
	return true;
}

